package org.smartboot.flow.core.component;


import org.smartboot.flow.core.Condition;
import org.smartboot.flow.core.EngineContext;
import org.smartboot.flow.core.Key;
import org.smartboot.flow.core.common.ComponentType;
import org.smartboot.flow.core.util.AssertUtil;
import org.smartboot.flow.core.visitor.ComponentVisitor;
import org.smartboot.flow.core.visitor.ConditionVisitor;

/**
 * @author qinluo
 * @date 2022-11-12 21:54:48
 * @since 1.0.0
 */
public class IfComponent<T, S> extends Component<T, S>{

    private Condition<T, S> condition;
    private Component<T, S> thenComponent;
    private Component<T, S> elseComponent;

    public IfComponent() {
    }

    public IfComponent(Condition<T, S> condition, Component<T, S> then) {
        this.condition = condition;
        this.thenComponent = (then);
    }

    public void setCondition(Condition<T, S> condition) {
        this.condition = condition;
    }

    public void setElseComponent(Component<T, S> elseComponent) {
        this.elseComponent = elseComponent;
    }

    public void setThenComponent(Component<T, S> thenComponent) {
        this.thenComponent = thenComponent;
    }

    @Override
    public int invoke(EngineContext<T, S> context) throws Throwable {
        context.enter(this);
        try {
            Object test = condition.test(context);
            Component<T, S> execute = null;

            if (test != null && Boolean.parseBoolean(String.valueOf(test))) {
                execute = this.thenComponent;
            } else if (elseComponent != null) {
                execute = this.elseComponent;
            }


            if (execute != null) {
                context.putExt(Key.of(this), execute);
                execute.invoke(context);
            }
        } finally {
            context.exit(this);
        }

        return 1;
    }

    @Override
    public boolean isRollbackable(EngineContext<T, S> context) {
        Component<T, S> executed = context.getExt(Key.of(this));
        return executed != null && executed.isRollbackable(context);
    }

    @Override
    public void rollback(EngineContext<T, S> context) {
        Component<T, S> executed = context.remove(Key.of(this));
        if (executed == null || !executed.isRollbackable(context)) {
            return;
        }

        context.enter(this);
        try {
            executed.rollback(context);
        } finally {
            context.exit(this);
        }
    }

    @Override
    public String describe() {
        return "if@" + condition.describe();
    }

    @Override
    public void visit(ComponentVisitor visitor) {
        visitor.visitAttributes(attributes);
        ConditionVisitor conditionVisitor = visitor.visitCondition(condition.describe());
        if (conditionVisitor != null) {
            condition.visit(conditionVisitor);
        }

        visitor.visitSource(this);
        ComponentVisitor thenVisitor = visitor.visitComponent(thenComponent.getType(), thenComponent.getName(), thenComponent.describe());
        if (thenVisitor != null) {
            thenComponent.visit(thenVisitor);
        }

        if (elseComponent != null) {
            ComponentVisitor elseVisitor = visitor.visitComponent(elseComponent.getType(), elseComponent.getName(), elseComponent.describe());
            if (elseVisitor != null) {
                elseComponent.visit(elseVisitor);
            }
        }
    }

    @Override
    public void doValidate() {
        AssertUtil.notNull(condition, "IF[" + getName() + "]condition must not be null");
        AssertUtil.notNull(thenComponent, "IF[" + getName() + "]branch must not be null");

        thenComponent.validate();
        if (elseComponent != null) {
            elseComponent.validate();
        }
    }

    @Override
    public ComponentType getType() {
        return ComponentType.IF;
    }
}
